﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Text.RegularExpressions;
using System.Security.Cryptography;

namespace WebSocketServer
{
    class WebSocketServer
    {
        static readonly string mask = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";  // 这是算法中要用到的固定字符串
        private static List<Socket> webSocketList = new List<Socket>();
        private static bool IsSendData;
        private static Dictionary<Socket, List<Thread>> ThreadDic = new Dictionary<Socket, List<Thread>>();

        public void OpenServer(string ip = "127.0.0.1", int port = 80)
        {
            Socket server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            server.Bind(new IPEndPoint(IPAddress.Parse(ip), port));     //IPAddress.Any
            server.Listen(1000);

            Console.WriteLine("服务器启动！" + server.LocalEndPoint);

            Thread t = new Thread(AcceptExecute);
            t.IsBackground = true;
            t.Start(server);
        }

        static void AcceptExecute(Object obj)
        {
            Socket server = obj as Socket;
            while (true)
            {
                Socket client = server.Accept();
                if (client != null)
                {
                    Console.WriteLine("客户端连接:" + client.RemoteEndPoint);
                    Thread t = new Thread(ReceiveExecute);
                    t.IsBackground = true;
                    t.Start(client);
                    AddThread(client, t);
                }
            }
        }

        static bool IsLinkWebSocket(Socket client, string msg)
        {
            if (webSocketList.Contains(client))
            {
                return false;
            }
            webSocketList.Add(client);

            Console.WriteLine("=====Handshaking from client=====\n{0}", msg);

            //string swk = Regex.Match(msg, "Sec-WebSocket-Key: (.*)").Groups[1].Value.Trim();
            //string swka = swk + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
            //byte[] swkaSha1 = System.Security.Cryptography.SHA1.Create().ComputeHash(Encoding.UTF8.GetBytes(swka));
            //string swkaSha1Base64 = Convert.ToBase64String(swkaSha1);

            //// HTTP/1.1 defines the sequence CR LF as the end-of-line marker
            //string response = "HTTP/1.1 101 Switching Protocols\r\n" +
            //    "Connection: Upgrade\r\n" +
            //    "Upgrade: websocket\r\n" +
            //    "Sec-WebSocket-Accept: " + swkaSha1Base64 + "\r\n\r\n";

            //获取 Sec-WebSocket-Key
            Regex regex = new Regex(@"Sec-WebSocket-Key: (?<key>[^\s]+)");
            string key = regex.Match(msg).Groups["key"].Value;

            Console.WriteLine("=====Handshaking from client key=====\n{0}\n", key);

            //key 和 mask 串接之后经过 SHA-1 处理，处理后的数据再经过一次 Base64 加密
            SHA1 sha1 = new SHA1CryptoServiceProvider();
            byte[] result = sha1.ComputeHash(Encoding.UTF8.GetBytes(key + mask));
            string Sec_WebSocket_Accept = Convert.ToBase64String(result);

            string response = "HTTP/1.1 101 Switching Protocols\r\n" +
                "Connection: Upgrade\r\n" +
                "Upgrade: websocket\r\n" +
                "Sec-WebSocket-Accept: " + Sec_WebSocket_Accept + "\r\n\r\n";

            client.Send(Encoding.UTF8.GetBytes(response), SocketFlags.None);

            Console.WriteLine("=====Response from server=====\n{0}", response);

            return true;
        }

        static void ReceiveExecute(Object obj)
        {
            Socket client = obj as Socket;
            while (true)
            {
                try
                {
                    byte[] buffer = new byte[1024];
                    int count = client.Receive(buffer, SocketFlags.None);

                    if (count > 0)
                    {
                        string msg = Encoding.UTF8.GetString(buffer, 0, count);

                        if (Regex.IsMatch(msg, "^GET", RegexOptions.IgnoreCase) || !webSocketList.Contains(client))
                        {
                            IsLinkWebSocket(client, msg);
                        }
                        else
                        {
                            decodeDataFrame(buffer, count, client);
                            if (!IsSendData)
                            {
                                IsSendData = true;
                                Thread t = new Thread(SendAsy);
                                t.IsBackground = true;
                                t.Start(client);
                                AddThread(client, t);
                            }
                        }
                    }
                }
                catch (Exception e)
                {
                    Console.WriteLine(e.Message);
                    CloseSocket(client, e.Message);
                }
            }
        }


        static void AddThread(Socket s, Thread t)
        {
            if (!ThreadDic.ContainsKey(s))
            {
                ThreadDic[s] = new List<Thread>();
            }
            ThreadDic[s].Add(t);
        }

        static void RemoveThread(Socket s)
        {
            if (ThreadDic.ContainsKey(s))
            {
                for (int i = 0; i < ThreadDic[s].Count; i++)
                {
                    ThreadDic[s][i].Abort();
                }
                ThreadDic.Remove(s);
            }
        }

        static void CloseSocket(Socket client, string errorMsg)
        {
            if (client != null)
                client.Close();
            RemoveThread(client);

            Console.WriteLine("CloseSocket:" + client.RemoteEndPoint.ToString() + errorMsg);
        }

        static void SendAsy(object obj)
        {
            Socket client = obj as Socket;

            //    for (int i = 0; i < 10; i++)
            //    {
            Thread.Sleep(1000);
            byte[] sendData = encodeDataFrame(DateTime.Now.ToString("yyyy-MM-dd HH-mm-ss"));
            client.Send(sendData, SocketFlags.None);
            Console.WriteLine("Send: " + DateTime.Now.ToString("yyyy-MM-dd HH-mm-ss"));
            //}
        }

        //数据帧的解析代码：      
        static string decodeDataFrame(byte[] buffer, int count, Socket client)
        {
            int type = buffer[0] & 0xF;

            if (type == 0x1) //文本
            {
                int mask = buffer[1] & 0x80;
                int len = buffer[1] & 0x7f;
                int dataLen = 0;
                int index = 2;

                if (len < 126)
                {
                    dataLen = len;
                }
                else if (len == 126)
                {
                    //+2
                    ByteToshort(ref dataLen, buffer);
                    index += 2;
                }
                else
                {
                    //+4
                    ByteToint(ref dataLen, buffer);
                    index += 4;
                }
                byte[] mark_Key = new byte[4];
                if (mask == 0x80)
                {
                    mark_Key = new byte[] { buffer[index], buffer[index + 1], buffer[index + 2], buffer[index + 3] };
                    index += 4;
                }
                if (count >= dataLen + index)
                {
                    byte[] real_Data = buffer.ToList().Skip(index).ToList().ToArray();
                    if (mask == 0x80)
                    {
                        for (int i = 0; i < real_Data.Length; i++)
                        {
                            real_Data[i] = (byte)(mark_Key[i % 4] ^ real_Data[i]);
                        }
                    }
                    Console.WriteLine("receive All: " + Encoding.UTF8.GetString(real_Data, 0, dataLen));
                    //剩余数据     

                }
                else
                {
                    //超长字段  不足

                    //byte[] data_all = new byte[dataLen];
                    //byte[] temp = new byte[1024];
                    //count = client.Receive(temp, SocketFlags.None);

                    //if (count > 0)
                    //{
                    //    decodeDataFrame(temp, count, client);
                    //}
                }
            }

            return null;
        }

        static void ByteToshort(ref int len, byte[] result)
        {
            len += (int)((result[2] << 8));
            len += (int)((result[3]));
        }

        static void ByteToint(ref int len, byte[] result)
        {
            len += (int)((result[2] << 24));
            len += (int)((result[3] << 16));
            len += (int)((result[4] << 8));
            len += (int)((result[5]));
        }


        //数据帧的编码：  服务器发送客户端不加密
        static byte[] encodeDataFrame(string msg)
        {
            byte[] data = Encoding.UTF8.GetBytes(msg);
            List<byte> list = new List<byte>();
            byte mask = 0x0; //0x80;
            list.Add(0x80 | 0x1);  // 0x80文本类型
            if (data.Length < 126)
            {
                list.Add((byte)(mask | data.Length));
            }
            else if (data.Length >= 126 && data.Length <= 65535) // 126==7e   65535==ffff
            {
                list.Add((byte)(mask | 126));
                list.AddRange(shortToByte(data.Length));
            }
            else
            {
                list.Add((byte)(mask | 127));
                list.AddRange(intToByte(data.Length));
            }
            if (mask == 0x80)
            {
                byte[] mark_Key = new byte[] { 0x1, 0x2, 0x3, 0x4 };
                for (int i = 0; i < data.Length; i++)
                {
                    data[i] = (byte)(mark_Key[i % 4] ^ data[i]);
                }

                list.AddRange(mark_Key);
            }

            list.AddRange(data);

            return list.ToArray();
        }

        static byte[] shortToByte(int len)
        {
            byte[] result = new byte[2];
            //Console.WriteLine((len >> 8) & 0xff);
            //Console.WriteLine((  8) & 0xff);
            result[0] = (byte)((len >> 8) & 0xff);
            result[1] = (byte)((len) & 0xff);

            return result;
        }

        static byte[] intToByte(int len)
        {
            byte[] result = new byte[4];

            result[0] = (byte)((len >> 24) & 0xff);
            result[1] = (byte)((len >> 16) & 0xff);
            result[2] = (byte)((len >> 8) & 0xff);
            result[3] = (byte)((len) & 0xff);

            return result;
        }

        /*      
            1byte
                bit: frame-fin，x0表示该message后续还有frame；x1表示是message的最后一个frame
                3bit: 分别是frame-rsv1、frame-rsv2和frame-rsv3，通常都是x0
                4bit: frame-opcode，x0表示是延续frame；x1表示文本frame；x2表示二进制frame；x3-7保留给非控制frame；x8表示关 闭连接；x9表示ping；xA表示pong；xB-F保留给控制frame
            2byte
                1bit: Mask，1表示该frame包含掩码；0，表示无掩码
                7bit、7bit+2byte、7bit+8byte: 7bit取整数值，若在0-125之间，则是负载数据长度；若是126表示，后两个byte取无符号16位整数值，是负载长度；127表示后8个 byte，取64位无符号整数值，是负载长度
            3-6byte: 这里假定负载长度在0-125之间，并且Mask为1，则这4个byte是掩码
            7-end byte: 长度是上面取出的负载长度，包括扩展数据和应用数据两部分，通常没有扩展数据；若Mask为1，则此数据需要解码，解码规则为1-4byte掩码循环和数据byte做异或操作。
            */
        /*
    0                   1                   2                   3
    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
    +-+-+-+-+-------+-+-------------+-------------------------------+
    |F|R|R|R| opcode|M| Payload len |    Extended payload length    |
    |I|S|S|S|  (4)  |A|     (7)     |             (16/64)           |
    |N|V|V|V|       |S|             |   (if payload len==126/127)   |
    | |1|2|3|       |K|             |                               |
    +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
    |     Extended payload length continued, if payload len == 127  |
    + - - - - - - - - - - - - - - - +-------------------------------+
    |                               |Masking-key, if MASK set to 1  |
    +-------------------------------+-------------------------------+
    | Masking-key (continued)       |          Payload Data         |
    +-------------------------------- - - - - - - - - - - - - - - - +
    :                     Payload Data continued ...                :
    + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
    |                     Payload Data continued ...                |
    +---------------------------------------------------------------+
         */


        /*   客户端请求
    GET /chat HTTP/1.1
    Host: server.example.com
    Upgrade: websocket
    Connection: Upgrade
    Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
    Origin: http://example.com
    Sec-WebSocket-Protocol: chat, superchat
    Sec-WebSocket-Version: 13
         */


        /*      服务端返回
    HTTP/1.1 101 Switching Protocols
    Upgrade: websocket
    Connection: Upgrade
    Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
    Sec-WebSocket-Protocol: chat
        */
    }
}
